package com.nulldev.util.web.YouTubeExtractor.extractors.official;

import java.io.InputStream;
import java.net.NoRouteToHostException;
import java.util.List;
import java.util.Map;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.JVM.Arguments.ArgumentInit;
import com.nulldev.util.JVM.Arguments.ParsedArguments;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.JVM.arguments.ArgumentWithValue.StringArgument;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.IExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.data.Arrays.arrays.FastLists;
import com.nulldev.util.internal.backport.concurrency9.Lists;
import com.nulldev.util.io.IOUtils;
import com.nulldev.util.io.FastIO.FastIO;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.web.HttpClient.HttpClient;
import com.nulldev.util.web.HttpClient.HttpResponse;
import com.nulldev.util.web.YouTubeExtractor.extractors.IYouTubeExtractor;
import com.nulldev.util.web.YouTubeExtractor.metadata.YouTubeMetadata;
import com.nulldev.util.web.YouTubeExtractor.video.ExtractedVideo;
import com.nulldev.util.web.YouTubeExtractor.video.ExtractionResult;
import com.nulldev.util.web.YouTubeExtractor.video.YouTubeVideo;

/**
 * This extractor is more of a proof-of-concept than an actual candidate for a
 * extractor. <br>
 * Reasons:
 * <ol>
 * <li>YouTube's Developer API doesn't provide any way to get the video streams
 * for playback.</li>
 * <li>YouTube's Developer API provides a limited amount of metadata that YTE
 * requires.</li>
 * <li>This API is more suited for analytics rather than YTE's or nTube's
 * use-case.</li>
 * </ol>
 * This means that this code will hang around unused for a while, and will
 * probably, with the passage of time, be either polished off or removed in the
 * future. <br>
 * <br>
 * <b>nTube and YTE are not planning to use this extractor.</b>
 * 
 * @author null
 */
public class YouTubeDataAPIExtractor extends IYouTubeExtractor {
	public static final int API_USED = 3;
	public static final String VERSION = "0.0.1";

	private static final IExecutorManager td = ExecutorManager.get();

	@Override
	public Executable<ExtractedVideo> generateTask(final Map<String, Object> arguments) {
		if (!isValidMap(arguments))
			throw new IllegalArgumentException("Invalid map: " + arguments);
		final String videoID = (String) arguments.get(MAP_TYPE_VIDEO_ID);
		if (!isValidVideoID(videoID))
			throw new IllegalArgumentException("Invalid video ID: " + videoID);
		return td.makeTask(() -> {
			final List<YouTubeVideo> videos = FastLists.list(OUTPUT_LIST_DEFAULT_SIZE);
			if (!arguments.containsKey(MAP_TYPE_YT_OFFICIAL_API_KEY)) {
				return new ExtractedVideo(videos, null, ExtractionResult.FAILED).setMessage("No API key was provided.");
			} else if (videoID == null || videoID.isEmpty()) {
				return new ExtractedVideo(videos, null, ExtractionResult.FAILED).setMessage("Invalid videoID was provided.");
			}
			final String apiKey = (String) arguments.get(MAP_TYPE_YT_OFFICIAL_API_KEY);
			if (apiKey.isEmpty()) {
				return new ExtractedVideo(videos, null, ExtractionResult.FAILED).setMessage("Invalid API key was provided.");
			}
			final String url = "https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics%2Cplayer&id=" + videoID + "&key="
					+ apiKey;
			final HttpResponse<InputStream> r;
			try {
				r = HttpClient.getStream().url(url).header("Accept", "application/json").create();
			} catch (Exception e1) {
				if (e1 instanceof NoRouteToHostException) {
					return new ExtractedVideo(null, null, ExtractionResult.FAILED).setMessage("FAILED_STAGE_UNABLE_TO_GET_METADATA")
							.setYTMessage("No route to youtube.com was found, this might be the internet.");
				} else
					return new ExtractedVideo(null, null, ExtractionResult.FAILED).setMessage("FAILED_STAGE_UNABLE_TO_GET_METADATA")
							.setYTMessage("Hit the following exception: " + e1.getMessage());
			}
			if (!r.successful()) {
				IOUtils.closeQuietly(r);
				return new ExtractedVideo(null, null, ExtractionResult.FAILED).setMessage("FAILED_STAGE_UNABLE_TO_GET_DATA")
						.setYTMessage("Hit the following response code: " + r.status());
			}
			final UniJsonValue u = UniJsonValue.parse(r.data());
			IOUtils.closeQuietly(r);
			u.println();
			if (u.contains("items")) {
				final UniJsonValue video = u.get("items").first();
				final UniJsonValue snippet = video.get("snippet");
				final YouTubeMetadata ytm = new YouTubeMetadata(videoID, snippet.getString("title"), snippet.getString("channelTitle"),
						snippet.getString("channelId"), 0, 0, 0, 0, 0, false, null, null);

				return new ExtractedVideo(Lists.of(), ytm, ExtractionResult.SUCCESS).setMessage("INFORMATIVE_LIMITED_API_DATA_AVAILABLE");
			} else {
				return new ExtractedVideo(null, null, ExtractionResult.FAILED).setMessage("FAILED_STAGE_UNABLE_TO_GET_DATA")
						.setYTMessage("No item array was found.");
			}
		});
	}

	@Override
	public Object getFlag(final int flag) {
		switch (flag) {
			case FLAG_EXTRACTOR_SUPPORTS_LIVESTREAM_CHAT:
			case FLAG_EXTRACTOR_SUPPORTS_LIVESTREAMS:
			case FLAG_EXTRACTOR_SUPPORTS_AGE_GATE:
				return FLAG_FALSE;
			case FLAG_EXTRACTOR_SUPPORTS_ABI_2_2:
				return FLAG_TRUE;
			case FLAG_EXTRACTOR_GET_SUPPORTED_ABI:
				return 2.2f;
			default:
				return FLAG_NOT_SUPPORTED;
		}
	}

	@Override
	public String getID() {
		return "official.data.api";
	}

	@Override
	public int abiVersion() {
		return 0;
	}

	public static void main(String[] args) {
		final StringArgument API_KEY_ARG = new StringArgument("--api-key", "The API key for YT Data API v3", "An API Key", false);
		Arguments.argumentInit(new ArgumentInit() {

			@Override
			public void onInit(Arguments args) {
				args.addArgument(API_KEY_ARG);
			}
		});
		Arguments.setup(args);
		final ParsedArguments pa = Arguments.awaitGet();
		if (!pa.has(API_KEY_ARG)) {
			JVM.println("ERROR: API KEY is missing!");
			FastIO.get().flush();
			JVM.exit();
			return;
		}
		final String API_KEY = pa.getValue(API_KEY_ARG);
		final String VIDEO_ID = "9q_nAZaLCZA";
		td.queueTask(new YouTubeDataAPIExtractor().generateTask(VIDEO_ID, "?key=" + API_KEY)).waitTillCompletion();
	}
}
